Rush - это библиотека эмуляции процессора PSX - R3000A, написанная Organic'ом, то есть мной :) изначально она расчитывалась, как _хардкорно_ оптимизированный интерпретатор, но за счет открытости интерфейса (я сам не ожидал такого xex), появилась возможность добавить динамическую рекомпиляцию (сейчас я ищу человека, который смог бы это сделать). скачать библиотеку пока нельзя.. еще не совсем готова :)

ИНИЦИАЛИЗАЦИЯ БИБЛИОТЕКИ

Rush - динамически подключаемая библиотека, содержащая 2 экспортируемых функции - RushInit и RushShutdown. для инициализации библиотеки, используется RushInit, которая возвращает указатель на структуру процессора R3000A, например:


/* пример инициализации Rush */

R3000A *cpu;   /* должен быть глобальным */

main()
{
   cpu = RushInit(RUSH_CPU_UNKNOWN, RUSH_HLE_DISABLE);	
   if(!cpu) SysError("can't initialize RushLibrary");

   /* другие действия */
   ...

   return 0;
}


первый параметр RushInit - тип используемой эмуляции:
второй параметр - флаги для эмулятора. по умолчанию их состояние - DISABLE. флаги могут объединяться, например RUSH_HLE_DISABLE|RUSH_RAND_ENABLE. можно подумать, что если какая-то опция выключена, то она является дохлым грузом для Rush. но на самом деле это не так: Rush заменит свои методы на те, в которых эта опция просто удалена, поэтому никаких потерь в скорости за счет проверок опций просто не существует. то есть тут работает принцип: хочешь использовать - используй (и тормози :) ). в последующих версиях Rush могут появиться еще флаги, например TLB или поддержка точек останова...
примечания: если вы используете RUSH_CPU_UNKNOWN, то Rush автоматически определит скорость вашего процессора и сам выберет тип эмуляции (интерпретатор или динамическую рекомпиляцию). насчет флагов: для стандартного эмулятора используйте комбинацию RUSH_HLE_ENABLE|RUSH_RAND_DISABLE. RUSH_CPU_DEBUGGER идеально подходит для разного рода отладчиков. он сохраняет все возможности интерпретации, при этом полностью (на сколько это возможно) эмулирует железо PSX. в этом режиме не работают никакие флаги: HLE всегда выключена, а эмуляция RAND и прочих функций (TLB, BP) включена. позднее он будет описан подробнее.

ИНИЦИАЛИЗАЦИЯ ПРОЦЕССОРА

для инициализации процессора, вызывается метод R3000A - Init:


/* пример инициализации процессора */

R3000A *cpu;   /* должен быть глобальным */

main()
{
   RUSH_RESULT res;

   /* инициализируем Rush и процессор */   
   cpu = RushInit(RUSH_CPU_UNKNOWN, RUSH_HLE_DISABLE);	
   if(!cpu) SysError("can't initialize RushLibrary");
   res = cpu->Init();
   if(res != RUSH_OKAY) SysError("can't initialize CPU");

   /* другие действия */
   ...

   return 0;
}


ЗАВЕРШЕНИЕ РАБОТЫ

для завершения работы, надо вначале уничтожить процессор, а потом Rush. делается это просто:


/* пример работы с Rush */

R3000A *cpu;   /* должен быть глобальным */

main()
{
   RUSH_RESULT res;

   /* инициализируем Rush и процессор */   
   cpu = RushInit(RUSH_CPU_UNKNOWN, RUSH_HLE_DISABLE);	
   if(!cpu) SysError("can't initialize RushLibrary");
   res = cpu->Init();
   if(res != RUSH_OKAY) SysError("can't initialize CPU");

   /* другие действия */
   ...

   /* уничтожаем вначале процессор, потом Rush */   
   cpu->Shutdown();
   RushShutdown();

   return 0;
}


ПОЛУЧЕНИЕ СВЕДЕНИЙ

чтобы получить необходимые данные, о состоянии эмуляции, используется метод R3000A - Get, например:

   char *name;

   /* получить имя используемой библиотеки Rush */
   name = (char *)cpu->Get(RUSH_GET_NAME);
параметр Get может принимать несколько значений, также, как и тип возвращаемых данных:

входной параметр
тип возвращаемых данных
описание
RUSH_GET_NAME (char *) получить имя библиотеки Rush, например "Rush PSX R3000A emulation library"
RUSH_GET_VERSION (char *) получить версию библиотеки Rush, например "1.0a"
RUSH_GET_CLOCK_COUNTER (unsigned long *) получить указатель на счетчик инструкций (тактов) процессора
RUSH_GET_PROGRAM_COUNTER (unsigned long) получить значение счетчика команд на текущий момент (НЕ указатель!)
RUSH_GET_INSTRUCTION (unsigned long *) получить указатель на текущую инструкцию процессора (только для интерпретатора)
RUSH_GET_CPU_REGISTERS (unsigned long *) получить указатель на 32 регитсра общего назначения (GPR)
RUSH_GET_COP0_REGISTERS (unsigned long *) получить указатель на 32 регитсра системного сопроцессора
RUSH_GET_HI_REGISTER (unsigned long *) получить указатель на регитср HI
RUSH_GET_LO_REGISTER (unsigned long *) получить указатель на регитср LO

примечание: параметр Get можно получить только один раз. далее его можно использовать в программе где угодно. например, получив указатель на счетчик тактов, его можно использовать без повторного получения. это не относится к RUSH_GET_PROGRAM_COUNTER. на самом деле, значение PC в Rush не хранит PSX адрес инструкции, поэтому оно должно правильно транслироваться назад в адресное пространство PSX..

ОПЕРАЦИИ С ПАМЯТЬЮ PSX

для получения прямого доступа к адресному пространству PSX, используются два метода структуры R3000A - Lock и UnLock:


/* пример прямого доступа к памяти PSX */

main()
{
   ...

   unsigned long *bios;
   
   /* получим указатель на BIOS */   
   bios = cpu->Lock(RUSH_LOCK_BIOS);
   /* операции с BIOS */   
   ...
   /* освободим доступ */   
   cpu->UnLock();

   ...
}


в принципе, если мы один раз залочили область памяти, то разлочивание пока ни к чему не приводит. связка LOCK-UNLOCK оставлена для будущего использования (а так бы эти методы были бы включены в Get). возможные области:
примечание: хотя с помощью этих методов можно получить полный доступ к аппаратным регистрам PSX, делать это не рекомендуется, так как теряется смысл использования аппаратных ловушек на эти регистры (см. далее). самое лучшее применение этих методов - загрузка файла с BIOS'ом.

УСТАНОВКА ЛОВУШЕК

Rush поддерживает установку ловушек на чтение/запись в регистры устройств (область памяти 1F801000-1F802FFF). так как регистры могут быть 8-, 16- и 32-битные, в Rush есть методы для каждого типа ловушек. для этого в структуре R3000A есть шесть методов - SetHook8, KillHook8, SetHook16, KillHook16 и SetHook32 c KillHook32.


/* пример использования ловушек */

/* функции чтения/записи в регистр статуса GPU */
/* находятся в эмуляторе */

unsigned long GPU_ReadStatus(void)
{
   /* используем плагин */
   return GPUreadStatus();
}

void GPU_WriteStatus(unsigned long gdata)
{
   /* используем плагин */
   GPUwriteStatus(gdata);
}

/* функции чтения/записи в регистр CDREG0 */
/* находятся в эмуляторе */

unsigned char CDR_Read0(void)
{
   /* тут эмялятор обслуживает доступ к регистру */
   ...
}

void CDR_Write0(unsigned char gdata)
{
   /* тут эмялятор обслуживает доступ к регистру */
   ...
}


main()
{
   RUSH_RESULT res;

   ...

   /* устанавливаем ловушки на регистр статуса GPU */
   res = cpu->SetHook32(0x1F801814, GPU_ReadStatus, GPU_WriteStatus);
   if(res != RUSH_OKAY) SysError("can't set hook on GPU status");
   /* а так ставим ловушки на регистры CDROM */
   res = cpu->SetHook8(0x1F801800, CDR_Read0, CDR_Write0);
   if(res != RUSH_OKAY) SysError("can't set hook on CDREG0");

   /* освобождаем ловушки */
   res = cpu->KillHook32(0x1F801814);
   if(res != RUSH_OKAY) SysError("can't clear hook on GPU status");
   res = cpu->KillHook8(0x1F801800);
   if(res != RUSH_OKAY) SysError("can't clear hook on CDREG0");

   ...
}



вот, на всякий случай, заголовоки SetHookX:
RUSH_RESULT SetHook8 (
   unsigned long addr,		// адрес регистра
   unsigned char (*ReadData)(void),	// ловушка на чтение
   void (*WriteData)(unsigned char)	// ловушка на запись
   );

RUSH_RESULT SetHook16 (
   unsigned long addr,		// адрес регистра
   unsigned short (*ReadData)(void),	// ловушка на чтение
   void (*WriteData)(unsigned short)	// ловушка на запись
   );

RUSH_RESULT SetHook32 (
   unsigned long addr,		// адрес регистра
   unsigned long (*ReadData)(void),	// ловушка на чтение
   void (*WriteData)(unsigned long)	// ловушка на запись
   );

возвращают RUSH_OKAY, если ловушка установлена, или RUSH_ERR, если нет.

ОБРАБОТКА ИСКЛЮЧЕНИЙ И ПРЕРЫВАНИЙ

эмуляция исключений и прерываний основана на том факте, что исключения происходят в процессоре, поэтому их обработка находится в Rush, а прерывания поступают от внешних устройств. поэтому обработку прерываний устройств эмулятор должен (если он хочет правильно работать) предоставить в распоряжение Rush. для этого существуют такие методы:

Exception - вызвать исключение процессора. у этого метода один параметр - тип исключения. из десятка с лишним возможных исключений у Rush используются только эти:
остальные исключения используются внутри Rush.

метод Interrupt должен быть инициализирован эмулятором. этот экспортируемый метод обычно работает со счетчиком тактов (clock counter). и обслуживает такие вещи, как ROOT COUNTERS, чтение CDROM и VSYNC.

ИНТЕГРАЦИЯ С СИСТЕМОЙ PSEmuPro

Rush может использоваться как расширение к системе плагинов PSEmuPro. для этого в состав Rush были включены еще 3 функции, стандартные для всех PSEmuPro-плагинов. подробнее про PSEmuPro, читай где-то на этом сайте.. только вот единственное различие между PSEmuPro и Rush в уровнях прослойки. если PSEmuPro принадлежит скорее к интерфейсу высшего уровня, то уровень Rush находится даже ниже, чем эмулятор. получается такой пирог: PSEmuPro->эмулятор->Rush. поэтому Rush нельзя назвать CPU-плагином, он скорее всего расширение...

THAT'S ALL

ну вот, в принципе, и все описание интерфейса библиотеки Rush, версии 1.0. в ближайше время появится и RushSDK.

назад...

Hosted by uCoz